import click, pathlib, cv2, time, logging
from typing import Any, Dict
from src.driver.usb_camera import StereoCamera_USB
from src.driver.pipeline import Pipeline
from src.utils.osd import OSD
from src.algorithms.stereo_matching import SGBMStereoMatcher
from src.infrastructure.sgbm_config import SGBMConfig
from src.infrastructure.logger import setupLogger
from src.infrastructure.config_watcher import ConfigWatcher


def setLoggersLevel(level: int | str) -> None:
    """
    统一设置当前进程内所有已创建 logger 的级别。

    参数:
        level: 日志级别，可接受 logging.DEBUG / "DEBUG" 等形式
    """
    # 规范化级别 -> 数字
    if isinstance(level, str):
        level = getattr(logging, level.upper())

    # 遍历所有真实 Logger（排除 PlaceHolder）
    for logger in logging.Logger.manager.loggerDict.values():
        if isinstance(logger, logging.Logger):
            logger.setLevel(level)


@click.group()
def cli():
    """双目 USB 相机控制工具"""
    pass


@cli.command()
@click.option("--left", "-l", default=0, help="左相机 ID")
@click.option("--right", "-r", default=1, help="右相机 ID")
@click.option(
    "--output", "-o", type=pathlib.Path, default="./images", help="图像保存目录"
)
@click.option("--pipeline", "-p", is_flag=True, help="实时运行立体匹配管道")
@click.option("--config", "-c", type=pathlib.Path, help="双目匹配参数 YAML(可选)")
@click.option("--watch", "-w", is_flag=True, help="热重载 YAML 配置")
@click.option("--verbose", "-v", is_flag=True, help="logger 全局 DEBUG")
def live(left, right, output, pipeline, config, watch, verbose):
    if verbose:
        logger = setupLogger("camera_cli", level="DEBUG")
        setLoggersLevel("DEBUG")
    else:
        logger = setupLogger("camera_cli")

    # ===== 1. 原有配置加载 =====
    sgbm_cfg = None
    if config:
        sgbm_cfg = SGBMConfig(config)
        _ = sgbm_cfg.load()
        matcher_params = sgbm_cfg.toMatcherParams()
        post_params = sgbm_cfg.toPostParams()
    else:

        matcher_params = SGBMStereoMatcher.getDefaultParams()
        post_params = {}

    # ===== 2. 实例化匹配器 =====
    matcher = SGBMStereoMatcher(**matcher_params)

    # ===== 3. 热重载线程 =====
    watcher = None
    if watch and config:

        def onDiff(diff: Dict[str, Any]) -> None:
            """处理配置变更

            Args:
                diff (dict): 变更的配置项
            """
            matcher_diff = {
                k.split(".", 1)[1]: v for k, v in diff.items() if k.startswith("sgbm.")
            }
            if matcher_diff:
                matcher.updateParams(**matcher_diff)
                logger.info(f"🔄 匹配器参数已热更新: {matcher_diff}")

        watcher = ConfigWatcher(config, callback=onDiff, interval=0.2)
        watcher.start()

    # ===== 4. 主循环 =====
    # cam = StereoCamera_USB(left, right)
    # pipe = Pipeline(cam, output)
    # pipe.start()
    osd = OSD()
    idx = 0
    t0 = time.time()
    try:
        while True:
            # pair = cam.grab()
            # fps = 1.0 / (time.time() - t0 + 1e-6)
            # t0 = time.time()

            # 实时立体匹配
            disp = None
            # if pipeline:
            #     disp = matcher.computeDisparity(pair)
            # TODO: 视差图后处理
            # TODO: 点云生成

            # 同一窗口左右可视化
            # osd.draw(pair, fps, cam.properties["drops"], "ESC:Quit  S:Save")
            # key = cv2.waitKey(1) & 0xFF
            # if key == 27:
            #     break
            # if key == ord("s"):
            #     pipe.saveOnce(pair, idx)  # 手动保存图片
            #     idx += 1
            #     logger.info(f"💾 手动保存图片对 #{idx}")
    finally:
        if watcher:
            watcher.stop()
        # pipe.stop()
        cv2.destroyAllWindows()


if __name__ == "__main__":
    cli()
